use std::{
    collections::HashMap,
    fmt::{Debug, Display},
    io::BufRead,
};

use tokio::process::Command;

use crate::{
    BandWidth, BandWidthArg, CommandExt, Impl, WiFiResult, check_command,
    os::iw_res::{Info, PhyInfo},
};

mod iw_res;

pub struct OS;

impl Impl for OS {
    async fn check_environment() -> crate::WiFiResult {
        check_command("iw").await?;
        check_command("ifconfig").await?;
        Ok(())
    }

    async fn interface_list() -> Result<Vec<crate::Interface>, crate::WiFiError> {
        let output = Command::new("sh")
            .arg("-c")
            .arg("iw dev | grep Interface")
            .output()
            .await
            .map_err(|e| crate::WiFiError::System(format!("Failed to execute command: {}", e)))?;

        let lines = output.stdout.lines();

        // 过滤和解析出网卡名称，这里假设网卡名称位于 "Interface <name>" 后面
        let mut interfaces = Vec::new();
        for line in lines.map_while(|i| i.ok()) {
            if let Some(interface_name) = line.split_whitespace().nth(1) {
                interfaces.push(crate::Interface {
                    id: interface_name.into(),
                    support_mode: vec![],
                });
            }
        }
        Ok(interfaces)
    }

    async fn set_mode(id: &ID, mode: crate::Mode) -> crate::WiFiResult {
        OS::ifdown(id).await?;
        Command::new("iw")
            .arg("dev")
            .arg(id.as_ref())
            .arg("set")
            .arg("type")
            .arg(mode.cmd())
            .execute(format!("[{id}] set mode `{}`", mode.cmd()))
            .await?;

        OS::ifup(id).await?;
        Ok(())
    }

    async fn set_channel(
        id: &ID,
        freq_mhz: usize,
        band_width: Option<BandWidthArg>,
    ) -> crate::WiFiResult {
        let mut cmd = Command::new("iw");
        let c = cmd
            .arg("dev")
            .arg(id.as_ref())
            .arg("set")
            .arg("channel")
            .arg(format!("{}", freq_mhz));

        if let Some(band_width) = band_width {
            c.arg(band_width.to_string());
        }

        cmd.execute(format!("set channel for interface {id}")).await
    }

    async fn ifup(id: &ID) -> crate::WiFiResult {
        Command::new("ifconfig")
            .arg(id.as_ref())
            .arg("up")
            .execute("bring up interface")
            .await
    }

    async fn ifdown(id: &ID) -> crate::WiFiResult {
        Command::new("ifconfig")
            .arg(id.as_ref())
            .arg("down")
            .execute("bring down interface")
            .await
    }

    async fn freq_max_bandwidth(id: &ID) -> WiFiResult<HashMap<usize, BandWidth>> {
        let mut out = HashMap::new();
        let info = info(id).await?;
        let output = Command::new("iw")
            .arg("phy")
            .arg(format!("phy{}", info.wiphy))
            .arg("info")
            .output()
            .await?;

        let result = String::from_utf8_lossy(&output.stdout);

        let info = PhyInfo::parse(&result)?;

        for band in info.bands {
            let mut max_width = BandWidth::HT20;
            if band.capabilities.rx_ht40_sgi {
                max_width = BandWidth::HT40;
            }
            if band.vht_capabilities.mhz80 {
                max_width = BandWidth::MHz80;
            }
            if band.vht_capabilities.mhz160 {
                max_width = BandWidth::MHz160;
            }

            for freq in band.frequencies {
                out.insert(freq as usize, max_width);
            }
        }

        Ok(out)
    }
}

async fn info(interface: &ID) -> WiFiResult<Info> {
    let output = Command::new("iw")
        .args(["dev", interface.as_ref(), "info"])
        .output()
        .await?;

    let result = String::from_utf8_lossy(&output.stdout);

    Info::parse(&result)
}

#[derive(Clone, Hash, Eq, PartialEq)]
pub struct ID {
    ifname: String,
}
impl Display for ID {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.ifname)
    }
}

impl Debug for ID {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "ID({})", self.ifname)
    }
}

impl From<String> for ID {
    fn from(ifname: String) -> Self {
        Self { ifname }
    }
}
impl From<&str> for ID {
    fn from(value: &str) -> Self {
        Self {
            ifname: value.to_string(),
        }
    }
}
impl AsRef<str> for ID {
    fn as_ref(&self) -> &str {
        self.ifname.as_str()
    }
}
